#include "display_screen.h"

// // 颜色转换宏：BMP的BGR格式 → ARGB8888（A=0：不透明）
// #define BGR2ARGB(a, r, g, b) ((a) << 24 | (r) << 16 | (g) << 8 | (b))
// // 最大支持的BMP文件数量
// #define MAX_FILES 100

// // BMP文件头结构体
// struct bitmap_header
// {
//     int16_t type;
//     int32_t size; // 图像文件大小
//     int16_t reserved1;
//     int16_t reserved2;
//     int32_t offbits; // BMP图像数据偏移量
// } __attribute__((packed));

// // BMP信息头结构体
// struct bitmap_info
// {
//     int32_t size;   // 本结构大小
//     int32_t width;  // 图像宽
//     int32_t height; // 图像高
//     int16_t planes;
//     int16_t bit_count; // 色深（仅支持24bpp）
//     int32_t compression;
//     int32_t size_img; // BMP文件中的RGB数据大小（4的整数倍）
//     int32_t X_pel;
//     int32_t Y_pel;
//     int32_t clrused;
//     int32_t clrImportant;
// } __attribute__((packed));

// // LCD设备结构体（双缓冲）
// struct LCD
// {
//     int fd;                    // 文件描述符
//     int width;                 // LCD像素宽
//     int height;                // LCD像素高
//     int pixle_byte;            // 每个像素字节数（色深/8）
//     int line_size;             // 1行字节数（width * pixle_byte）
//     unsigned long screen_size; // 单屏总字节数
//     char *fb_mem;              // 双缓冲总起始地址（char*：兼容所有色深）
// };

// /**
//  * @brief  获取指定文件后缀的路径，存放在数组中
//  * @param  appoint_dir   指定目录
//  * @param  appoint_type  文件后缀（如".bmp"）
//  * @param  allfile_way   存放路径的二维数组
//  * @return 成功：文件数量；失败：-1
//  */
// int get_bmp_location(const char *appoint_dir, const char *appoint_type, char (*allfile_way)[256])
// {
//     DIR *dir_stream = opendir(appoint_dir);
//     if (dir_stream == NULL)
//     {
//         char temp[256];
//         int len = snprintf(temp, sizeof(temp), "[%d] opendir ( %s ) failed: %s",
//                            __LINE__, appoint_dir, strerror(errno));
//         if (len >= sizeof(temp))
//             fprintf(stderr, "警告：错误信息被截断！\n");
//         fprintf(stderr, "%s\n", temp);
//         return -1;
//     }

//     struct dirent *dir_info;
//     int appoint_num = 0;
//     while (1)
//     {
//         dir_info = readdir(dir_stream);
//         if (errno != 0 && dir_info == NULL)
//         {
//             perror("readdir failed");
//             closedir(dir_stream);
//             return -1;
//         }
//         if (dir_info == NULL)
//             break; // 读完退出

//         // 跳过隐藏文件（.开头）和.、..
//         if (dir_info->d_name[0] == '.')
//             continue;

//         // 匹配文件类型并存储路径
//         if (appoint_type == NULL)
//         {
//             snprintf(allfile_way[appoint_num++], 256, "%s/%s", appoint_dir, dir_info->d_name);
//         }
//         else
//         {
//             if (strstr(dir_info->d_name, appoint_type))
//             {
//                 int sp_num = snprintf(allfile_way[appoint_num], 256, "%s/%s", appoint_dir, dir_info->d_name);
//                 if (sp_num >= 256)
//                     fprintf(stderr, "警告：路径 %s/%s 被截断！\n", appoint_dir, dir_info->d_name);
//                 appoint_num++;
//             }
//         }
//     }

//     closedir(dir_stream);
//     return appoint_num;
// }

// /**
//  * @brief  绘制单个像素（支持任意色深，指定缓冲基地址）
//  * @param  lcd        LCD设备结构体
//  * @param  fb_base    缓冲基地址（后台缓冲）
//  * @param  x/y        像素坐标
//  * @param  color      像素颜色（ARGB8888）
//  */
// void draw_pixle(struct LCD *lcd, char *fb_base, int x, int y, int color)
// {
//     if (x < 0 || x >= lcd->width || y < 0 || y >= lcd->height)
//         return;

//     char *pix_addr = fb_base + (y * lcd->line_size) + (x * lcd->pixle_byte); // 乘4字节每列

//     if (lcd->pixle_byte == 4)
//         *(int *)pix_addr = color;
// }

// /**
//  * @brief  显示BMP图片到指定缓冲
//  * @param  lcd        LCD设备结构体
//  * @param  bmpname    BMP文件名
//  * @param  xoffset/yoffset 显示偏移
//  * @param  fb_base    绘制目标缓冲（后台缓冲）
//  */
// void show_bmp(struct LCD *lcd, char *bmpname, int xoffset, int yoffset, char *fb_base)
// {
//     if (!lcd || !bmpname || !fb_base)
//         return;

//     int bmp_fd = open(bmpname, O_RDONLY);
//     if (bmp_fd < 0)
//     {
//         fprintf(stderr, "open() %s fail: %s\n", bmpname, strerror(errno));
//         return;
//     }

//     struct bitmap_header bhead;
//     struct bitmap_info binfo;
//     read(bmp_fd, &bhead, sizeof(bhead));
//     read(bmp_fd, &binfo, sizeof(binfo));

//     // 仅支持24bpp BMP（若需支持其他色深，需补充逻辑）
//     if (binfo.bit_count != 24)
//     {
//         fprintf(stderr, "不支持的BMP色深：%d位（仅支持24位）\n", binfo.bit_count);
//         close(bmp_fd);
//         return;
//     }

//     // 从BMP图片中获取整个图像大小，然后动态分配数据
//     char *bmp_buf = malloc(binfo.size_img);
//     if (!bmp_buf)
//     {
//         perror("malloc bmp_buf failed");
//         close(bmp_fd);
//         return;
//     }
//     // 前面两次读取光标已经偏移
//     read(bmp_fd, bmp_buf, binfo.size_img);

//     // 计算显示范围（避免超界）
//     int w = binfo.width;
//     int h = binfo.height;
//     int line_size = binfo.size_img / binfo.height;
//     char *bmp_tmp = bmp_buf;

//     // 调整x方向显示范围（不超过LCD宽度800）
//     if (xoffset + w > 800)
//         w = 800 - xoffset;
//     // 调整y方向显示范围（不超过LCD高度480）
//     if (yoffset + h > 480)
//     {
//         int offset_y = yoffset + h - 480;
//         h = 480 - yoffset;
//         bmp_tmp += line_size * offset_y;
//     }

//     // 逐像素绘制（BMP是上下颠倒的，需反转y坐标）
//     for (int y = 0; y < h; y++)
//     {
//         int k = 0;
//         for (int x = 0; x < w; x++)
//         {
//             int color = BGR2ARGB(0, bmp_tmp[k + 2], bmp_tmp[k + 1], bmp_tmp[k]);
//             draw_pixle(lcd, fb_base, x + xoffset, (h - y - 1) + yoffset, color);
//             k += 3;
//         }
//         bmp_tmp += line_size;
//     }

//     free(bmp_buf);
//     close(bmp_fd);
// }

// /**
//  * @brief  LCD双缓冲初始化
//  * @param  lcd        LCD设备结构体
//  * @return 成功：0；失败：-1
//  */
// int lcd_init(struct LCD *lcd)
// {
//     // 打开Framebuffer设备
//     lcd->fd = open("/dev/fb0", O_RDWR);
//     if (lcd->fd < 0)
//     {
//         perror("open() /dev/fb0 fail");
//         return -1;
//     }

//     // 读取LCD参数
//     struct fb_var_screeninfo varinfo;
//     if (ioctl(lcd->fd, FBIOGET_VSCREENINFO, &varinfo) != 0)
//     {
//         perror("ioctl() FBIOGET_VSCREENINFO fail");
//         close(lcd->fd);
//         return -1;
//     }

//     // 初始化LCD属性（假设LCD分辨率800x480，色深32bpp）
//     lcd->width = varinfo.xres;
//     lcd->height = varinfo.yres;
//     lcd->pixle_byte = varinfo.bits_per_pixel / 8;
//     lcd->line_size = lcd->width * lcd->pixle_byte;
//     lcd->screen_size = lcd->line_size * lcd->height;

//     // 申请双缓冲内存（2*单屏大小）
//     char *p = mmap(NULL, 2 * lcd->screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, lcd->fd, 0);
//     if (p == MAP_FAILED)
//     {
//         perror("mmap() 双缓冲内存失败");
//         close(lcd->fd);
//         return -1;
//     }
//     lcd->fb_mem = p;
//     bzero(lcd->fb_mem, 2 * lcd->screen_size); // 初始化内存为0（黑色）

//     // 初始可见区设为B区（双缓冲）
//     varinfo.xoffset = 0;
//     varinfo.yoffset = lcd->height;
//     if (ioctl(lcd->fd, FBIOPAN_DISPLAY, &varinfo) != 0)
//     {
//         perror("ioctl() FBIOPAN_DISPLAY fail");
//         munmap(lcd->fb_mem, 2 * lcd->screen_size);
//         close(lcd->fd);
//         return -1;
//     }

//     printf("LCD初始化成功：%dx%d，%d字节/像素\n",
//            lcd->width, lcd->height, lcd->pixle_byte);
//     return 0;
// }

// /**
//  * @brief  LCD反初始化（释放资源）
//  * @param  lcd        LCD设备结构体
//  * @return 成功：0；失败：-1
//  */
// int lcd_uninit(struct LCD *lcd)
// {
//     if (lcd->fb_mem != MAP_FAILED)
//     {
//         munmap(lcd->fb_mem, 2 * lcd->screen_size);
//         lcd->fb_mem = MAP_FAILED;
//     }
//     if (lcd->fd >= 0)
//     {
//         close(lcd->fd);
//         lcd->fd = -1;
//     }
//     return 0;
// }

// // 显示一张图片,给出指定位置,
// int bg_change = 0;

// void display_a_photo(struct LCD *lcd, char *bmp_path)
// {
//     char *bg_buf = (*lcd).fb_mem + (*lcd).screen_size * (bg_change % 2);

//     ioctl((*lcd).fd, FBIOGET_VSCREENINFO, &varinfo); // 读取初始参数
//     show_bmp(lcd, bmp_path, 0, 0, bg_buf);
//     varinfo.yoffset = (*lcd).height * (bg_change % 2);
//     ioctl((*lcd).fd, FBIOPAN_DISPLAY, &varinfo);
//     // sleep(1); // 每张图显示1秒
//     // bg_change++;
//     // if (bg_change == 2)
//     //     bg_change = 0;
// }
//----------------------------------------
/**
 * @brief  获取指定文件后缀的路径，存放在数组中
 * @param  appoint_dir   指定目录
 * @param  appoint_type  文件后缀（如".bmp"）
 * @param  allfile_way   存放路径的二维数组
 * @return 成功：文件数量；失败：-1
 */
int get_bmp_location(const char *appoint_dir, const char *appoint_type, char (*allfile_way)[256])
{
    DIR *dir_stream = opendir(appoint_dir);
    if (dir_stream == NULL)
    {
        char temp[256];
        int len = snprintf(temp, sizeof(temp), "[%d] opendir ( %s ) failed: %s",
                           __LINE__, appoint_dir, strerror(errno));
        if (len >= sizeof(temp))
            fprintf(stderr, "警告：错误信息被截断！\n");
        fprintf(stderr, "%s\n", temp);
        return -1;
    }

    struct dirent *dir_info;
    int appoint_num = 0;
    while (1)
    {
        dir_info = readdir(dir_stream);
        if (errno != 0 && dir_info == NULL)
        {
            perror("readdir failed");
            closedir(dir_stream);
            return -1;
        }
        if (dir_info == NULL)
            break; // 读完退出

        // 跳过隐藏文件（.开头）和.、..
        if (dir_info->d_name[0] == '.')
            continue;

        // 匹配文件类型并存储路径
        if (appoint_type == NULL)
        {
            snprintf(allfile_way[appoint_num++], 256, "%s/%s", appoint_dir, dir_info->d_name);
        }
        else
        {
            if (strstr(dir_info->d_name, appoint_type))
            {
                int sp_num = snprintf(allfile_way[appoint_num], 256, "%s/%s", appoint_dir, dir_info->d_name);
                if (sp_num >= 256)
                    fprintf(stderr, "警告：路径 %s/%s 被截断！\n", appoint_dir, dir_info->d_name);
                appoint_num++;
            }
        }
    }

    closedir(dir_stream);
    return appoint_num;
}

/**
 * @brief  绘制单个像素（支持任意色深，指定缓冲基地址）
 * @param  lcd        LCD设备结构体
 * @param  fb_base    缓冲基地址（后台缓冲）
 * @param  x/y        像素坐标
 * @param  color      像素颜色（ARGB8888）
 */
void draw_pixle(struct LCD *lcd, char *fb_base, int x, int y, int color)
{
    if (x < 0 || x >= lcd->width || y < 0 || y >= lcd->height)
        return;

    char *pix_addr = fb_base + (y * lcd->line_size) + (x * lcd->pixle_byte);

    if (lcd->pixle_byte == 4)
        *(int *)pix_addr = color;
}

/**
 * @brief  显示BMP图片到指定缓冲（后台缓冲绘制，不直接显示）
 * @param  lcd        LCD设备结构体
 * @param  bmpname    BMP文件名
 * @param  xoffset/yoffset 显示偏移
 * @param  fb_base    绘制目标缓冲（后台缓冲）
 */
void show_bmp(struct LCD *lcd, char *bmpname, int xoffset, int yoffset, char *fb_base)
{
    if (!lcd || !bmpname || !fb_base)
        return;

    int bmp_fd = open(bmpname, O_RDONLY);
    if (bmp_fd < 0)
    {
        fprintf(stderr, "open() %s fail: %s\n", bmpname, strerror(errno));
        return;
    }

    struct bitmap_header bhead;
    struct bitmap_info binfo;
    read(bmp_fd, &bhead, sizeof(bhead));
    read(bmp_fd, &binfo, sizeof(binfo));

    // 仅支持24bpp BMP
    if (binfo.bit_count != 24)
    {
        fprintf(stderr, "不支持的BMP色深：%d位（仅支持24位）\n", binfo.bit_count);
        close(bmp_fd);
        return;
    }

    // 动态分配BMP数据缓冲区
    char *bmp_buf = malloc(binfo.size_img);
    if (!bmp_buf)
    {
        perror("malloc bmp_buf failed");
        close(bmp_fd);
        return;
    }
    read(bmp_fd, bmp_buf, binfo.size_img); // 读取BMP像素数据

    // 计算显示范围（避免超出LCD屏幕）
    int w = binfo.width;
    int h = binfo.height;
    int line_size = binfo.size_img / binfo.height; // BMP每行字节数（含对齐）
    char *bmp_tmp = bmp_buf;

    // 调整X方向显示范围（不超过LCD宽度）
    if (xoffset + w > lcd->width)
        w = lcd->width - xoffset;
    // 调整Y方向显示范围（不超过LCD高度）
    if (yoffset + h > lcd->height)
    {
        int offset_y = yoffset + h - lcd->height;
        h = lcd->height - yoffset;
        bmp_tmp += line_size * offset_y; // 跳过超出部分的BMP数据
    }

    // 逐像素绘制到【后台缓冲】（BMP数据是上下颠倒的，需反转Y坐标）
    for (int y = 0; y < h; y++)
    {
        int k = 0;
        for (int x = 0; x < w; x++)
        {
            // BMP是BGR格式，转换为ARGB8888
            int color = BGR2ARGB(0, bmp_tmp[k + 2], bmp_tmp[k + 1], bmp_tmp[k]);
            draw_pixle(lcd, fb_base, x + xoffset, (h - y - 1) + yoffset, color);
            k += 3; // 24bpp：每个像素3字节（B、G、R）
        }
        bmp_tmp += line_size; // 跳到BMP下一行（含对齐字节）
    }

    free(bmp_buf);
}

/**
 * @brief  LCD双缓冲初始化（申请2倍屏幕大小内存，初始化显示参数）
 * @param  lcd        LCD设备结构体
 * @return 成功：0；失败：-1
 */
int lcd_init(struct LCD *lcd)
{
    // 1. 打开Framebuffer设备（Linux帧缓冲设备，用于直接操作LCD显存）
    lcd->fd = open("/dev/fb0", O_RDWR);
    if (lcd->fd < 0)
    {
        perror("open() /dev/fb0 fail（需root权限）");
        return -1;
    }

    // 2. 读取LCD硬件参数（分辨率、色深等）
    struct fb_var_screeninfo varinfo;
    if (ioctl(lcd->fd, FBIOGET_VSCREENINFO, &varinfo) != 0)
    {
        perror("ioctl() FBIOGET_VSCREENINFO fail");
        close(lcd->fd);
        return -1;
    }

    // 3. 初始化LCD双缓冲核心参数
    lcd->width = varinfo.xres;                       // LCD实际宽度（如800）
    lcd->height = varinfo.yres;                      // LCD实际高度（如480）
    lcd->pixle_byte = varinfo.bits_per_pixel / 8;    // 每个像素字节数（32bpp→4字节）
    lcd->line_size = lcd->width * lcd->pixle_byte;   // 单屏每行字节数
    lcd->screen_size = lcd->line_size * lcd->height; // 单个缓冲总字节数

    // 4. 申请双缓冲内存（映射2倍屏幕大小到用户空间，避免内核态/用户态拷贝）
    lcd->fb_mem = mmap(
        NULL,                   // 自动分配地址
        2 * lcd->screen_size,   // 大小：2个缓冲（前台+后台）
        PROT_READ | PROT_WRITE, // 读写权限
        MAP_SHARED,             // 共享映射（修改同步到硬件）
        lcd->fd,                // 关联的Framebuffer设备
        0                       // 偏移量（从显存起始地址开始）
    );
    if (lcd->fb_mem == MAP_FAILED)
    {
        perror("mmap() 双缓冲内存失败（内存不足或权限不足）");
        close(lcd->fd);
        return -1;
    }

    // 5. 初始化双缓冲为黑色（避免初始乱码）
    bzero(lcd->fb_mem, 2 * lcd->screen_size);

    // 6. 设置初始显示区为【缓冲2】（后续绘制先在缓冲1进行，实现“后台绘制”）
    varinfo.yoffset = lcd->height; // yoffset=高度 → 显示缓冲2（缓冲1：yoffset=0；缓冲2：yoffset=height）
    if (ioctl(lcd->fd, FBIOPAN_DISPLAY, &varinfo) != 0)
    {
        perror("ioctl() FBIOPAN_DISPLAY fail（不支持双缓冲？）");
        munmap(lcd->fb_mem, 2 * lcd->screen_size);
        close(lcd->fd);
        return -1;
    }

    printf("LCD双缓冲初始化成功：%dx%d，%d字节/像素，双缓冲总大小：%luKB\n",
           lcd->width, lcd->height, lcd->pixle_byte,
           (2 * lcd->screen_size) / 1024);
    return 0;
}

/**
 * @brief  LCD双缓冲反初始化（释放内存和设备资源）
 * @param  lcd        LCD设备结构体
 * @return 成功：0；失败：-1
 */
int lcd_uninit(struct LCD *lcd)
{
    // 释放双缓冲内存映射
    if (lcd->fb_mem != MAP_FAILED)
    {
        munmap(lcd->fb_mem, 2 * lcd->screen_size);
        lcd->fb_mem = MAP_FAILED;
    }
    // 关闭Framebuffer设备
    if (lcd->fd >= 0)
    {
        close(lcd->fd);
        lcd->fd = -1;
    }
    printf("LCD双缓冲资源已释放\n");
    return 0;
}

// 显示一张图片,给出指定位置,
int bg_change = 0;
void display_a_photo(struct LCD *lcd, char *bmp_path)
{
    if (!lcd || !bmp_path || lcd->fb_mem == MAP_FAILED)
    {
        fprintf(stderr, "display_a_photo() 参数无效或LCD未初始化\n");
        return;
    }
    char *bg_buf = (*lcd).fb_mem + (*lcd).screen_size * (bg_change % 2);

    ioctl((*lcd).fd, FBIOGET_VSCREENINFO, &varinfo); // 读取初始参数
    show_bmp(lcd, bmp_path, 0, 0, bg_buf);
    varinfo.yoffset = (*lcd).height * (bg_change % 2);
    ioctl((*lcd).fd, FBIOPAN_DISPLAY, &varinfo);
    // sleep(1); // 每张图显示1秒
    // bg_change++;
    // if (bg_change == 2)
    //     bg_change = 0;
}

int bg_change2 = 0; // 双缓冲切换全局标志

void display_a_photo2(struct LCD *lcd, char *bmp_path, int xoffset, int yoffset)
{
    if (!lcd || !bmp_path || lcd->fb_mem == MAP_FAILED)
    {
        fprintf(stderr, "display_a_photo2() 参数无效或LCD未初始化\n");
        return;
    }

    // 1. 确定当前后台缓冲地址
    int buf_idx = bg_change2 % 2;
    char *bg_buf = lcd->fb_mem + buf_idx * lcd->screen_size;

    // 2. 读取LCD参数
    struct fb_var_screeninfo varinfo;
    if (ioctl(lcd->fd, FBIOGET_VSCREENINFO, &varinfo) != 0)
    {
        perror("display_a_photo2() ioctl FBIOGET_VSCREENINFO fail");
        return;
    }

    // 3. 绘制BMP到该后台缓冲
    show_bmp(lcd, bmp_path, xoffset, yoffset, bg_buf);

    // 4. 切换可见区到该后台缓冲
    varinfo.yoffset = buf_idx * lcd->height;
    if (ioctl(lcd->fd, FBIOPAN_DISPLAY, &varinfo) != 0)
    {
        perror("display_a_photo2() ioctl FBIOPAN_DISPLAY fail");
        return;
    }

    // 5. 更新切换标志
    bg_change2++;
}
// 获取某个像素点的数据
int get_bmp_pixel_color(char *bmpname, int target_x, int target_y)
{
    int bmp_fd = open(bmpname, O_RDONLY);
    if (bmp_fd < 0)
    {
        fprintf(stderr, "open() %s fail: %s\n", bmpname, strerror(errno));
        return -1; // 返回错误码
    }

    struct bitmap_header bhead;
    struct bitmap_info binfo;
    read(bmp_fd, &bhead, sizeof(bhead));
    read(bmp_fd, &binfo, sizeof(binfo));

    // 仅支持24bpp BMP
    if (binfo.bit_count != 24)
    {
        fprintf(stderr, "不支持的BMP色深：%d位（仅支持24位）\n", binfo.bit_count);
        close(bmp_fd);
        return -1;
    }

    // 检查坐标是否越界 (使用 binfo.width/height)
    if (target_x < 0 || target_x >= binfo.width || target_y < 0 || target_y >= binfo.height)
    {
        fprintf(stderr, "坐标(%d, %d)超出图像范围 (%dx%d)\n",
                target_x, target_y, binfo.width, binfo.height);
        close(bmp_fd);
        return -1;
    }

    // 从BMP图片中获取整个图像大小，然后动态分配数据
    char *bmp_buf = malloc(binfo.size_img);
    if (!bmp_buf)
    {
        perror("malloc bmp_buf failed");
        close(bmp_fd);
        return -1;
    }
    // 前面两次读取光标已经偏移
    read(bmp_fd, bmp_buf, binfo.size_img);

    // --- 开始修正核心逻辑 ---

    // 1. 计算BMP图像每行的实际字节数 (考虑行对齐)
    //    BMP规定每一行的字节数必须是4的倍数
    int line_size = ((binfo.width * 3 + 3) / 4) * 4;

    // 2. BMP图像数据是上下颠倒存储的。
    //    所以需要将传入的Y坐标（从上往下）转换为数据中的行索引（从下往上）。
    int data_y = binfo.height - 1 - target_y;

    // 3. 计算目标像素在 bmp_buf 中的起始地址
    //    地址 = 行起始地址 + 像素在该行内的偏移
    //    行起始地址 = data_y * line_size
    //    像素偏移 = target_x * 3 (因为每个像素3个字节)
    char *pixel_ptr = bmp_buf + (data_y * line_size) + (target_x * 3);

    // 4. 读取BGR颜色分量
    unsigned char blue = pixel_ptr[0];
    unsigned char green = pixel_ptr[1];
    unsigned char red = pixel_ptr[2];

    // 5. 使用宏转换颜色格式
    int color = BGR2ARGB(0, red, green, blue);

    // --- 修正结束 ---

    // 清理
    free(bmp_buf);
    close(bmp_fd);

    return color;
}
